#  ______     ______     ______     ______     ______   __     ______     ______
# /\  ___\   /\  == \   /\  ___\   /\  __ \   /\__  _\ /\ \   /\___  \   /\  ___\
# \ \ \____  \ \  __<   \ \  __\   \ \  __ \  \/_/\ \/ \ \ \  \/_/  /__  \ \  __\
#  \ \_____\  \ \_\ \_\  \ \_____\  \ \_\ \_\    \ \_\  \ \_\   /\_____\  \ \_____\
#   \/_____/   \/_/ /_/   \/_____/   \/_/\/_/     \/_/   \/_/   \/_____/   \/_____/

# Code is licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
# https://creativecommons.org/licenses/by-nc-nd/4.0/
# You CANNOT edit this file without direct permission from the author.
# You can redistribute this file without any changes.

# meta developer: @creaz_mods
# scope: hikka_min 1.6.2
# reqires: geopy

from ast import literal_eval
from functools import partial

from geopy import distance
from geopy.geocoders import Nominatim
from hikkatl import functions, types
from hikkatl.types import Message

from .. import loader, utils


@loader.tds
class GeoTools(loader.Module):
    """Some geographic tools"""

    strings = {
        "name": "GeoTools",
        "no_args": (
            "<emoji document_id=5260342697075416641>❌</emoji> <b>You did not specify"
            " arguments</b>"
        ),
        "no_place": (
            "<emoji document_id=5260342697075416641>❌</emoji> <b>Place {} not"
            " found</b>"
        ),
        "result": (
            "<emoji document_id=5413689758440237842>▶️</emoji> From: {fromd}\n<emoji"
            " document_id=5413557786980134319>▶️</emoji> To: {to}\n<emoji"
            " document_id=5877464772353460958>✈️</emoji> Distance: {dist} kilometers"
        ),
        "langcode": "en",
        "point": (
            "<emoji document_id=5460795800101594035>🗣️</emoji> <b>Your query:</b>"
            " {args}\n<emoji document_id=5206607081334906820>✔️</emoji> <b>Result:</b>"
            " <code>{res}</code>"
        ),
    }

    strings_ru = {
        "no_args": (
            "<emoji document_id=5260342697075416641>❌</emoji> <b>Вы не указали"
            " аргументы</b>"
        ),
        "no_place": (
            "<emoji document_id=5260342697075416641>❌</emoji> <b>Место {} не"
            " найдено</b>"
        ),
        "result": (
            "<emoji document_id=5413689758440237842>▶️</emoji> Из: {fromd}\n<emoji"
            " document_id=5413557786980134319>▶️</emoji> В: {to}\n<emoji"
            " document_id=5877464772353460958>✈️</emoji> Расстояние: {dist} километров"
        ),
        "langcode": "ru",
        "point": (
            "<emoji document_id=5460795800101594035>🗣️</emoji> <b>Ваш запрос:</b>"
            " {args}\n<emoji document_id=5206607081334906820>✔️</emoji>"
            " <b>Результат:</b> <code>{res}</code>"
        ),
    }

    strings_es = {
        "no_args": (
            "<emoji document_id = 5260342697075416641>❌</emoji> <b>No especificaste"
            " argumentos</b>"
        ),
        "no_place": (
            "<emoji document_id=5260342697075416641>❌</emoji> <b>No se encontró el"
            " lugar {}</b>"
        ),
        "result": (
            "<emoji document_id = 5413689758440237842>▶️</emoji> Desde: {fromd}\n<emoji"
            " document_id = 5413557786980134319>▶️</emoji> A: {to}\n<emoji"
            " document_id=5877464772353460958>✈️</emoji> Distancia: {dist} kilómetros"
        ),
        "langcode": "es",
        "point": (
            "<emoji document_id=5460795800101594035>🗣️</emoji> <b>Su consulta:</b>"
            " {args}\n<emoji document_id=5206607081334906820>✔️</emoji>"
            " <b>Resultado:</b> <code>{res}</code>"
        ),
    }

    strings_de = {
        "no_args": (
            "<emoji document_id=5260342697075416641>❌</emoji> <b>Sie haben keine"
            " Argumente angegeben</b>"
        ),
        "no_place": (
            "<emoji document_id=5260342697075416641>❌</emoji> <b>No se encontró el"
            " lugar {}</b>"
        ),
        "result": (
            "<emoji document_id=5413689758440237842>▶️</emoji> Von: {fromd}\n<emoji"
            " document_id=5413557786980134319>▶️</emoji> Nach: {to}\n<emoji"
            " document_id=5877464772353460958>✈️</emoji> Entfernung: {dist} Kilometer"
        ),
        "langcode": "de",
        "point": (
            "<emoji document_id=5460795800101594035>🗣️</emoji> <b>Ihre Abfrage:</b>"
            " {args}\n<emoji document_id=5206607081334906820>✔️</emoji> <b>Ergebnis:</b>"
            " <code>{res}</code>"
        ),
    }

    @loader.command(
        ru_doc=(
            "- определяет расстояние между двумя точками. Можно указать координаты в"
            " скобках, можно сразу указать место. Разделять два места через |"
        ),
        es_doc=(
            "- determina la distancia entre dos puntos. Puede especificar las"
            " coordenadas entre paréntesis o puede especificar el lugar directamente."
            " Separe los dos lugares con |"
        ),
        de_doc=(
            "- ermittelt die Entfernung zwischen zwei Punkten. Sie können die"
            " Koordinaten in Klammern angeben oder den Ort direkt angeben. Trennen Sie"
            " die beiden Orte mit einem |"
        ),
    )
    async def geodist(self, m: Message):
        """- specifies the distance between two points. You can specify the coordinates in brackets or just a place. Split two places via |"""
        args = utils.get_args_split_by(m, "|")

        if len(args) != 2:
            return await utils.answer(m, self.strings["no_args"])

        client = Nominatim(user_agent="Hikka")
        geocode = partial(client.geocode, language=self.strings["langcode"])
        reverse = partial(client.reverse, language=self.strings["langcode"])

        try:
            args[0] = tuple(literal_eval(args[0]))
            args[1] = tuple(literal_eval(args[1]))

            place0 = geocode(args[0])
            place1 = geocode(args[1])

            if not place0:
                return await utils.answer(m, self.strings["no_place"].format(args[0]))
            if not place1:
                return await utils.answer(m, self.strings["no_place"].format(args[1]))

            from_coord = args[0]
            to_coord = args[1]
            dist = round(distance.distance(from_coord, to_coord).km, 2)
            from_address = reverse(from_coord).address
            to_address = reverse(to_coord).address

            await utils.answer(
                m,
                self.strings["result"].format(
                    fromd=from_address, to=to_address, dist=dist
                ),
            )
        except:
            place0 = geocode(args[0])
            place1 = geocode(args[1])

            if not place0:
                return await utils.answer(m, self.strings["no_place"].format(args[0]))
            if not place1:
                return await utils.answer(m, self.strings["no_place"].format(args[1]))

            from_coord = (
                place0.latitude,
                place1.longitude,
            )
            to_coord = (
                place0.latitude,
                place1.longitude,
            )
            dist = round(distance.distance(from_coord, to_coord).km, 2)
            await utils.answer(
                m,
                self.strings["result"].format(
                    fromd=place0,
                    to=place1,
                    dist=dist,
                ),
            )

    @loader.command(
        ru_doc="- отправляет полное название места",
        de_doc="- sendet den vollständigen Namen eines Ortes",
        es_doc="- envía el nombre completo de una ubicación",
    )
    async def point(self, m: Message):
        """Sends the full name of a location"""
        args = utils.get_args_raw(m)

        if not (args):
            return await utils.answer(m, self.strings["no_args"])

        client = Nominatim(user_agent="Hikka")
        geocode = partial(client.geocode, language=self.strings["langcode"])

        place = geocode(args)
        if not place:
            return await utils.answer(m, self.strings["no_place"].format(args))

        await utils.answer(m, self.strings["point"].format(args=args, res=place))

    @loader.command(
        ru_doc="- отправляет координаты места",
        de_doc="- sendet die Koordinaten des Ortes",
        es_doc="- envía las coordenadas del lugar",
    )
    async def coords(self, m: Message):
        """- sends the coordinates of the location"""
        args = utils.get_args_raw(m)

        if not args:
            return await utils.answer(m, self.strings["no_args"])

        client = Nominatim(user_agent="Hikka")
        geocode = partial(client.geocode, language=self.strings["langcode"])
        place = geocode(args)

        if not place:
            return await utils.answer(m, self.strings["no_place"].format(args))

        await utils.answer(
            m,
            self.strings["point"].format(
                args=args, res=f"{place.latitude}, {place.longitude}"
            ),
        )

    @loader.command(
        ru_doc="- отправляет геопозицию",
        es_doc="- envía la ubicación geográfica",
        de_doc="- sendet den Geostandort",
    )
    async def geo(self, m: Message):
        """- sends geoposition"""
        args = utils.get_args_raw(m)
        if await m.get_reply_message():
            r = await m.get_reply_message()
        else:
            r = m

        if not args:
            return await utils.answer(m, self.strings["no_args"])

        client = Nominatim(user_agent="Hikka")
        geocode = partial(client.geocode, language=self.strings["langcode"])
        if "-c" in args:
            args = args.replace("-c", "")
            place = tuple(literal_eval(args))
        else:
            args = args.replace("-c", "")
            place = geocode(args)
            place = (place.latitude, place.longitude)

        if not place:
            return await utils.answer(m, self.strings["no_place"].format(args))

        await m.delete()

        await m.client(
            functions.messages.SendMediaRequest(
                peer=m.chat_id,
                media=types.InputMediaGeoPoint(
                    geo_point=types.InputGeoPoint(place[0], place[1])
                ),
                message="",
                reply_to_msg_id=r.id,
            )
        )
